﻿
#if USE_WPF

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;

#endif

using Bling.Ops;
using Bling.Util;
using Bling.DSL;
using Bling.Core;
using linq = Microsoft.Linq.Expressions;


#if USE_WPF



namespace Bling.Core {
  using Bling.WPF;
  using Bling.Vecs;

  public abstract partial class BaseValueBrand<T, BRAND, K, D> : ValueBrand<T, BRAND>, IValueBrandKD<K, D>
    where BRAND : ValueBrand<T, BRAND, K, D>
    where D : Dim<D> {

    public static void SetWPFProperties<WPF>(params MemberInfo[] infos) {
      WPFConversions<K, D, WPF>.Properties = infos;
      CanWrap<BRAND, WPF>.From = e => WPFConversions<K, D, WPF>.ToWPF.Instance.Make(e);
      CanWrap<BRAND, WPF>.To = e => ToBrandD(WPFConversions<K, D, WPF>.ToBling.Instance.Make(e));
      WPFConversions.WPFToBling[typeof(WPF)] = typeof(T);
    }
  }

  public partial class Double2Bl : PointDoubleBl<D2, Double2Bl>, IHasXY<double, DoubleBl> {
    private static readonly int InitWPF0 = InitWPF();
    private static int InitWPF() {
      SetWPFProperties<Point>(
        typeof(Point).GetProperty("X"),
        typeof(Point).GetProperty("Y")
      );
      SetWPFProperties<Vector>(
        typeof(Vector).GetProperty("X"),
        typeof(Vector).GetProperty("Y")
      );
      SetWPFProperties<Size>(
        typeof(Size).GetProperty("Width"),
        typeof(Size).GetProperty("Height")
      );
      return 0;
    }
    public static implicit operator Double2Bl(Expr<Point> p) {
      return WPFConversions<double,D2, Point>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Point>(Double2Bl p) {
      return WPFConversions<double,D2, Point>.ToWPF.Instance.Make(p);
    }
    public static implicit operator Double2Bl(Expr<Vector> p) {
      return WPFConversions<double, D2, Vector>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Vector>(Double2Bl p) {
      return WPFConversions<double, D2, Vector>.ToWPF.Instance.Make(p);
    }
    public static implicit operator Double2Bl(Expr<Size> p) {
      return WPFConversions<double, D2, Size>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Size>(Double2Bl p) {
      return WPFConversions<double, D2, Size>.ToWPF.Instance.Make(p);
    }
    public static implicit operator Double2Bl(Point p) {
      return new Constant<Point>(p);
    }
    public static implicit operator Double2Bl(Vector p) {
      return new Constant<Vector>(p);
    }
    public static implicit operator Double2Bl(Size p) {
      return new Constant<Size>(p);
    }
    /*
    public static Double2Bl Table(Point[] Points, IntBl Index) {
      return Table<Point>(Points, Index, WPFConversions<double, D2, Point>.ToBling.Instance.Make);
    }*/
  }
  public partial class Double3Bl : PointDoubleBl<D3, Double3Bl>, IHasXYZ<double, DoubleBl> {
    private static readonly int InitWPF0 = InitWPF();
    private static int InitWPF() {
      SetWPFProperties<Point3D>(
        typeof(Point3D).GetProperty("X"),
        typeof(Point3D).GetProperty("Y"),
        typeof(Point3D).GetProperty("Z")
      );
      SetWPFProperties<Vector3D>(
        typeof(Vector3D).GetProperty("X"),
        typeof(Vector3D).GetProperty("Y"),
        typeof(Vector3D).GetProperty("Z")
      );
      return 0;
    }
    public static implicit operator Double3Bl(Expr<Point3D> p) {
      return WPFConversions<double, D3, Point3D>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Point3D>(Double3Bl p) {
      return WPFConversions<double, D3, Point3D>.ToWPF.Instance.Make(p);
    }
    public static implicit operator Double3Bl(Expr<Vector3D> p) {
      return WPFConversions<double, D3, Vector3D>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Vector3D>(Double3Bl p) {
      return WPFConversions<double, D3, Vector3D>.ToWPF.Instance.Make(p);
    }
    public static implicit operator Double3Bl(Point3D p) {
      return new Constant<Point3D>(p);
    }
    public static implicit operator Double3Bl(Vector3D p) {
      return new Constant<Vector3D>(p);
    }
    /*
    public static Double3Bl Table(Point3D[] Points, IntBl Index) {
      return Table<Point3D>(Points, Index, WPFConversions<double, D3, Point3D>.ToBling.Instance.Make);
    }*/
  }
  public partial class Double4Bl : FixDoubleBl<D4, Double4Bl>, IHasXYZW<double, DoubleBl> {
    private static readonly int InitWPF0 = InitWPF();
    private static int InitWPF() {
      SetWPFProperties<Point4D>(
        typeof(Point4D).GetProperty("X"),
        typeof(Point4D).GetProperty("Y"),
        typeof(Point4D).GetProperty("Z"),
        typeof(Point4D).GetProperty("W")
      );
      SetWPFProperties<Rect>(
        typeof(Rect).GetProperty("X"),
        typeof(Rect).GetProperty("Y"),
        typeof(Rect).GetProperty("Width"),
        typeof(Rect).GetProperty("Height")
      );
      return 0;
    }
    /*
    public static Double4Bl Table(Point4D[] Points, IntBl Index) {
      return Table<Point4D>(Points, Index, WPFConversions<double, D4, Point4D>.ToBling.Instance.Make);
    }*/


    public static implicit operator Double4Bl(Expr<Point4D> p) {
      return WPFConversions<double, D4, Point4D>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Point4D>(Double4Bl p) {
      return WPFConversions<double, D4, Point4D>.ToWPF.Instance.Make(p);
    }
    public static implicit operator Double4Bl(Expr<Rect> p) {
      return WPFConversions<double, D4, Rect>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Rect>(Double4Bl p) {
      return WPFConversions<double, D4, Rect>.ToWPF.Instance.Make(p);
    }
    public static implicit operator Double4Bl(Point4D p) {
      return new Constant<Point4D>(p);
    }
    public static implicit operator Double4Bl(Rect p) {
      return new Constant<Rect>(p);
    }
  }
  public partial class ColorBl : DoubleBl<IVec<double, D4>, ColorBl, D4>, IHasXYZW<double, DoubleBl> {
    private static readonly int InitWPF0 = InitWPF();
    private static int InitWPF() {
      SetWPFProperties<Color>(
        typeof(Color).GetProperty("ScR"),
        typeof(Color).GetProperty("ScG"),
        typeof(Color).GetProperty("ScB"),
        typeof(Color).GetProperty("ScA")
      );
      return 0;
    }
    public static implicit operator ColorBl(Expr<Color> p) {
      return WPFConversions<double, D4, Color>.ToBling.Instance.Make(p);
    }
    public static implicit operator Expr<Color>(ColorBl p) {
      return WPFConversions<double, D4, Color>.ToWPF.Instance.Make(p);
    }
    public static implicit operator ColorBl(Color p) { return new Constant<Color>(p); }
  }
  public partial class RgbBl : DoubleBl<IVec<double, D3>, RgbBl, D3>, IHasXYZ<DoubleBl> {
    public static implicit operator RgbBl(Color p) {
      return ((ColorBl)p).ScRGB;
    }
  }

}
namespace Bling.Core {
  using Bling.WPF;
  public static partial class CoreExtensions {
    public static Double2Bl Bl(this Expr<Point> p) { return p; }
    public static Double2Bl Bl(this Expr<Vector> p) { return p; }
    public static Double2Bl Bl(this Expr<Size> p) { return p; }
    public static Double3Bl Bl(this Expr<Point3D> p) { return p; }
    public static Double3Bl Bl(this Expr<Vector3D> p) { return p; }
    public static Double4Bl Bl(this Expr<Point4D> p) { return p; }
    public static Double4Bl Bl(this Expr<Rect> p) { return p; }
    public static ColorBl Bl(this Expr<Color> p) { return p; }

    public static Double2Bl Bl(this Point p) { return p; }
    public static Double2Bl Bl(this Vector p) { return p; }
    public static Double2Bl Bl(this Size p) { return p; }
    public static Double3Bl Bl(this Point3D p) { return p; }
    public static Double3Bl Bl(this Vector3D p) { return p; }
    public static Double4Bl Bl(this Point4D p) { return p; }
    public static Double4Bl Bl(this Rect p) { return p; }
    public static ColorBl Bl(this Color p) { return p; }


  }
}
namespace Bling.Graphics {
  using Bling.Core;
  using Bling.Vecs;
  using Bling.Shaders;
  /* XXX: add later.
  public partial class Tex2D : BaseTex<Double2Bl, Tex2D> {
    public static explicit operator Tex2D(Color Clr) { return (Tex2D)((ColorBl)Clr); }
  }*/
}


#endif

namespace Bling.WPF {
  using Microsoft.Linq.Expressions;
  using Bling.Core;
  using Bling.Vecs;
  public interface IWPFEval<EVAL> : IEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<WPF> ToWPF<K,D, WPF>(Expr<IVec<K,D>> Arg, MemberInfo[] Properties, WPF Prototype) where D : Dim<D>;
    Eval<EVAL>.Info<IVec<K, D>> ToBling<K, D, WPF>(Expr<WPF> Arg, MemberInfo[] Properties, WPF Prototype) where D : Dim<D>;
  }
  public static partial class WPFConversions {
    public static readonly Dictionary<Type, Type> WPFToBling = new Dictionary<Type, Type>();
  }

  public partial class WPFConversions<K,D,WPF> where D : Dim<D> {
    internal static MemberInfo[] Properties;
    internal static readonly WPF Prototype;

    static WPFConversions() {
      var Constructor = typeof(WPF).GetConstructor(new Type[] { });
      if (Constructor != null) Prototype = (WPF)Constructor.Invoke(new object[] { });
      else Prototype = default(WPF);
    }
    public partial class ToWPF : Ops.Operator<IVec<K,D>, WPF, ToWPF> {
      public static new readonly ToWPF Instance = new ToWPF();
      private ToWPF() { }
      public override Eval<EVAL>.Info<WPF> Eval<EVAL>(Operation<IVec<K, D>, WPF> op, Eval<EVAL> txt) {
        if (txt is IWPFEval<EVAL>) 
          return ((IWPFEval<EVAL>) txt).ToWPF<K,D,WPF>(op.ArgA, Properties, Prototype);
        return base.Eval<EVAL>(op, txt);
      }
      public override Expr<IVec<K, D>> Inverse(Expr<IVec<K, D>> argA, Expr<WPF> result) {
        return ToBling.Instance.Make(result);
      }
    }
    public partial class ToBling : Ops.Operator<WPF, IVec<K, D>, ToBling> {
      public static new readonly ToBling Instance = new ToBling();
      private ToBling() { }
      public override Func<Expression, Expression> Linq(Operation<WPF, IVec<K, D>> op) {


        return base.Linq(op);
      }

      public override Eval<EVAL>.Info<IVec<K, D>> Eval<EVAL>(Operation<WPF, IVec<K, D>> op, Eval<EVAL> txt) {
        if (txt is IWPFEval<EVAL>)
          return ((IWPFEval<EVAL>)txt).ToBling<K,D,WPF>(op.ArgA, Properties, Prototype);
        return base.Eval<EVAL>(op, txt);
      }
      public override Expr<WPF> Inverse(Expr<WPF> argA, Expr<IVec<K, D>> result) {
        return ToWPF.Instance.Make(result);
      }
    }
  }
}